package boardgamer.yavalath.client.animation;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import boardgamer.common.client.Drawable;
import boardgamer.common.client.PeriodicTimer;
import boardgamer.common.client.animation.Animation;
import boardgamer.common.client.animation.AnimationCollection;
import boardgamer.common.client.layout.MutableBitmapLayout;
import boardgamer.yavalath.client.Globals;
import boardgamer.yavalath.client.resources.ImageHandler;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * @author garysoed
 */
public class YavalathBackground implements Drawable {

  private static final int ADD_HEX_PERIOD = 1000;
  private static final float MAX_WIDTH_SCREEN_WIDTH_RATIO = 0.5f;

  private final HexSpawner hexSpawner;
  private final AnimationCollection hexes;
  private final Rect screenRect;
  private final float scale;

  public YavalathBackground(Rect screenRect) {
    this.screenRect = checkNotNull(screenRect, "screenRect");

    this.hexSpawner = new HexSpawner();
    this.hexes = new AnimationCollection();
    this.scale = screenRect.width() * MAX_WIDTH_SCREEN_WIDTH_RATIO
        / ImageHandler.backgroundHex5.getWidth();
  }

  @Override
  public void draw(Canvas canvas) {
    hexes.draw(canvas);
  }

  public void update(int elapsedTime) {
    hexSpawner.update(elapsedTime);
    hexes.update(elapsedTime);
  }

  private class HexSpawner extends PeriodicTimer {

    private Bitmap[] bitmaps;

    protected HexSpawner() {
      super(ADD_HEX_PERIOD);
      this.bitmaps = new Bitmap[] {
          ImageHandler.backgroundHex1,
          ImageHandler.backgroundHex2,
          ImageHandler.backgroundHex3,
          ImageHandler.backgroundHex4,
          ImageHandler.backgroundHex5
      };
    }

    @Override
    protected void onPeriodic() {
      float y = Globals.RANDOM.nextFloat() * screenRect.height();
      float step = 128 * y / screenRect.height();
      int color = Color.rgb(Math.round(255 - step), 0, Math.round(step));

      Hex hex = new Hex(
          Globals.RANDOM.nextFloat() * screenRect.width(),
          y,
          color,
          bitmaps[Globals.RANDOM.nextInt(5)],
          scale);
      hexes.addTemporary(hex);
    }
  }

  private static class Hex extends Animation implements Drawable {

    private static final int UPDATE_PERIOD = 37;
    private static final int EXPAND_STEP = 10;
    private static final int FADE_STEP = 400;

    private enum State {
      FADING_IN,
      FADING_OUT,
      DRAWN
    }

    private final float centerX;
    private final RectF targetRect;
    private final RectF bitmapRect;
    private final Paint bitmapPaint;
    private final MutableBitmapLayout bitmapLayout;

    private State state;

    public Hex(float centerX, float centerY, int color, Bitmap bitmap, float scale) {
      super(UPDATE_PERIOD);
      this.centerX = centerX;

      this.state = State.FADING_IN;

      float targetHalfWidth = bitmap.getWidth() * scale / 2;
      float targetHalfHeight = bitmap.getHeight() * scale / 2;
      this.targetRect = new RectF(
          centerX - targetHalfWidth,
          centerY - targetHalfHeight,
          centerX + targetHalfWidth,
          centerY + targetHalfHeight);
      this.bitmapRect = new RectF(
          centerX,
          targetRect.top,
          centerX,
          targetRect.bottom);

      this.bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      this.bitmapPaint.setColorFilter(new LightingColorFilter(color, 0));
      this.bitmapPaint.setAlpha(255);

      this.bitmapLayout = new MutableBitmapLayout(bitmap, bitmapPaint);
      this.bitmapLayout.setBoundingRect(bitmapRect);
    }

    @Override
    public void draw(Canvas canvas) {
      bitmapLayout.draw(canvas);
    }

    @Override
    public boolean isDead() {
      return state == State.DRAWN;
    }

    @Override
    protected void onPeriodic() {
      switch (state) {
        case FADING_IN:
          float width = bitmapRect.width() + targetRect.width() / EXPAND_STEP;
          if (width >= targetRect.width()) {
            width = targetRect.width();
            state = State.FADING_OUT;
          }

          bitmapRect.left = centerX - width / 2;
          bitmapRect.right = centerX + width / 2;
          bitmapLayout.setBoundingRect(bitmapRect);
          break;
        case FADING_OUT:
          float alpha = bitmapPaint.getAlpha() - (float) 255 / FADE_STEP;
          if (alpha <= 0) {
            alpha = 0;
            state = State.DRAWN;
          }

          bitmapPaint.setAlpha(Math.round(alpha));
          bitmapLayout.setPaint(bitmapPaint);
          break;
        case DRAWN:
          break;
      }
    }
  }
}
